/*
@title: Enigma
@description: "Enigma" is a simulation game focused on encoding and decoding messages using a simulated Enigma machine. Players can configure the machine's wheels and plugs, then input letters to encrypt or decrypt messages.
@author: Ben Gertler
@tags: ['sandbox','simulation']
@addedOn: 2024-08-28
*/

/*
BASIC CONTROLS:
WASD to move the cursor (the red box).
Note that you can't always move the cursor in every direction.
L to select.
J to go back.

MAPS:
Main Menu: Table with a note and the Enigma machine.
Note: Shows the current objective. Will update once correctly (de)ciphered.
Enigma Config: Menu with machine configuration options.
Enigma: Main machine. Select letters to (de)cipher them. X to clear ciphertext.
Explain: Turn on step-by-step descriptions of Enigma cipher, progressed by selecting.
Wheels: Use arrows to rotate wheels. Select two numbers at the top to swap wheel positions.
Plugs: Select plug 1 or plug 2 circles, then select two letters to plug together. X to clear.

ENIGMA CIPHER:
  1. If input is on a plug, swap to the other letter with the same plug.
  2. Send through wheel in position 1 (right).
Wheels are rotating scramblers with 26 unique paths, one for each letter.
The path taken is determined by the input letter and the wheel's rotation.
An input of "a" on an unrotated wheel (rotation a) will take path a.
An input of "a" on a rotation b wheel will take path b.
  3. Send through wheel in position 2 (middle).
  4. Send through wheel in position 3 (left).
  5. Send through reflector, changing letter and sending it in reverse.
Signal goes left to right now, so all wheel paths are reversed.
  6. Send through wheel in position 3.
  7. Send through wheel in position 2.
  8. Send through wheel in position 1.
  9. If output is on a plug, swap to other letter with the same plug.
  10. Rotate wheels.
Usually, only position 1 rotates. If 1 rotates from z to a, position 2 rotates.
Same with positions 2 and 3.
  11. Tada! You Enigmatized a letter.

You can find much better explanations if you're interested.

TIPS:
* Remember, the rightmost wheel is position 1. i.e. starting wheel position is 1, 2, 3.
* Wheel order and rotation on the note will correspond to right to left wheels.
* Try not to exit out of Enigma while on explain mode until you see the ciphertext at the top.
* Since wheels rotate on each input, you have to set them back if you try to redo something.
* Just because you don't see ciphertext doesn't mean it's gone. The display scrolls automatically.
* If only a few letters are incorrect, you may be inputting the wrong letters or your plugs may be off.
* There won't be any indication you've completed the note; you have to check it to see.
* The note has four sequential tasks. Try to do them all!
*/

//tile definitions
const cursor = "C"
const reflectorTile = "R"
const button = "B"
const divider = "D"
const arrowL = "<"
const arrowR = ">"
const redX = "X"
const checkmark = "*"
const enigma = "E"
const note = "N"
const table = "T"
const a = "a"
const b = "b"
const c = "c"
const d = "d"
const e = "e"
const f = "f"
const g = "g"
const h = "h"
const i = "i"
const j = "j"
const k = "k"
const l = "l"
const m = "m"
const n = "n"
const o = "o"
const p = "p"
const q = "q"
const r = "r"
const s = "s"
const t = "t"
const u = "u"
const v = "v"
const w = "w"
const x = "x"
const y = "y"
const z = "z"
//1, 2, and 3 are here, but variables don't work so well there. remember to put in string
const plugTile1 = "("
const plugTile2 = ")"
const scramTop = "["
const scramMid = "|"
const scramBot = "]"
const light = "L"
const blank = "-"

setLegend(
  [cursor, bitmap`
3333333333333333
3333333333333333
33............33
33............33
33............33
33............33
33............33
33............33
33............33
33............33
33............33
33............33
33............33
33............33
3333333333333333
3333333333333333`],
  [reflectorTile, bitmap`
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111
1111111111111111`],
  [button, bitmap`
....00000000....
...0........0...
..0..........0..
.0............0.
0..............0
0..............0
0..............0
0..............0
0..............0
0..............0
0..............0
0..............0
.0............0.
..0..........0..
...0........0...
....00000000....`],
  [divider, bitmap`
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000
0000000000000000`],
  [arrowL, bitmap`
................
................
................
................
................
......00........
.....00.........
....00..........
...00...........
....00..........
.....00.........
......00........
................
................
................
................`],
  [arrowR, bitmap`
................
................
................
................
................
........00......
.........00.....
..........00....
...........00...
..........00....
.........00.....
........00......
................
................
................
................`],
  [redX, bitmap`
................
................
.33..........33.
..33........33..
...33......33...
....33....33....
.....33..33.....
......3333......
.......33.......
......3333......
.....33..33.....
....33....33....
...33......33...
..33........33..
.33..........33.
................`],
  [checkmark, bitmap`
................
................
................
................
.............D..
............DD..
...........DD...
..........DD....
..D......DD.....
..DD....DD......
...DD..DD.......
....DDDD........
.....DD.........
................
................
................`],
  [enigma, bitmap`
................
..000000000000..
..022222222220..
..02122L2L2L20..
..02122L2L2L20..
..02122L2L2L20..
..022222222220..
..011111111110..
..011111111110..
..011111111110..
..000000000000..
..011111111110..
..011111111110..
..011111111110..
..000000000000..
................`],
  [note, bitmap`
................
................
................
................
........2.......
.......222......
......22222.....
.....2222222....
....2222222.....
.....22222......
......222.......
.......2........
................
................
................
................`],
  [table, bitmap`
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCC`],
  [a, bitmap`
................
................
................
................
................
................
................
.....00000......
.........00.....
.....000000.....
....00...00.....
.....0000.00....
................
................
................
................`],
  [b, bitmap`
................
................
................
................
................
....000.........
.....00.........
.....00000......
.....00..00.....
.....00..00.....
.....00..00.....
....00.000......
................
................
................
................`],
  [c, bitmap`
................
................
................
................
................
................
................
.....0000.......
....00..00......
....00..........
....00..00......
.....0000.......
................
................
................
................`],
  [d, bitmap`
................
................
................
................
................
.......000......
........00......
.....00000......
....00..00......
....00..00......
....00..00......
.....000.00.....
................
................
................
................`],
  [e, bitmap`
................
................
................
................
................
................
................
.....0000.......
....00..00......
....000000......
....00..........
.....0000.......
................
................
................
................`],
  [f, bitmap`
................
................
................
................
................
......000.......
.....00.00......
.....00.........
....0000........
.....00.........
.....00.........
....0000........
................
................
................
................`],
  [g, bitmap`
................
................
................
................
................
................
................
................
.....000.00.....
....00..00......
....00..00......
.....00000......
........00......
....00000.......
................
................`],
  [h, bitmap`
................
................
................
................
................
....000.........
.....00.........
.....00.00......
.....000.00.....
.....00..00.....
.....00..00.....
....000..00.....
................
................
................
................`],
  [i, bitmap`
................
................
................
................
................
......00........
................
.....000........
......00........
......00........
......00........
....000000......
................
................
................
................`],
  [j, bitmap`
................
................
................
................
................
........00......
................
.......000......
........00......
........00......
....00..00......
....00..00......
.....0000.......
................
................
................`],
  [k, bitmap`
................
................
................
................
................
....000.........
.....00.........
.....00..00.....
.....00.00......
.....0000.......
.....00.00......
....000..00.....
................
................
................
................`],
  [l, bitmap`
................
................
................
................
................
.....000........
......00........
......00........
......00........
......00........
......00........
....000000......
................
................
................
................`],
  [m, bitmap`
................
................
................
................
................
................
................
....00..00......
....0000000.....
....0000000.....
....00.0.00.....
....00...00.....
................
................
................
................`],
  [n, bitmap`
................
................
................
................
................
................
................
....00000.......
....00..00......
....00..00......
....00..00......
....00..00......
................
................
................
................`],
  [o, bitmap`
................
................
................
................
................
................
................
.....0000.......
....00..00......
....00..00......
....00..00......
.....0000.......
................
................
................
................`],
  [p, bitmap`
................
................
................
................
................
................
................
................
....00.000......
.....00..00.....
.....00..00.....
.....00000......
.....00.........
....0000........
................
................`],
  [q, bitmap`
................
................
................
................
................
................
................
................
.....000.00.....
....00..00......
....00..00......
.....00000......
........00......
.......0000.....
................
................`],
  [r, bitmap`
................
................
................
................
................
................
................
....00.000......
.....000.00.....
.....00..00.....
.....00.........
....0000........
................
................
................
................`],
  [s, bitmap`
................
................
................
................
................
................
................
.....00000......
....00..........
.....0000.......
........00......
....00000.......
................
................
................
................`],
  [t, bitmap`
................
................
................
................
................
......0.........
.....00.........
....00000.......
.....00.........
.....00.........
.....00.0.......
......00........
................
................
................
................`],
  [u, bitmap`
................
................
................
................
................
................
................
....00..00......
....00..00......
....00..00......
....00..00......
.....000.00.....
................
................
................
................`],
  [v, bitmap`
................
................
................
................
................
................
................
....00..00......
....00..00......
....00..00......
.....0000.......
......00........
................
................
................
................`],
  [w, bitmap`
................
................
................
................
................
................
................
....00...00.....
....00.0.00.....
....0000000.....
....0000000.....
.....00.00......
................
................
................
................`],
  [x, bitmap`
................
................
................
................
................
................
................
....00...00.....
.....00.00......
......000.......
.....00.00......
....00...00.....
................
................
................
................`],
  [y, bitmap`
................
................
................
................
................
................
................
................
....00..00......
....00..00......
....00..00......
.....00000......
........00......
....00000.......
................
................`],
  [z, bitmap`
................
................
................
................
................
................
................
....000000......
....0..00.......
......00........
.....00..0......
....000000......
................
................
................
................`],
  ["1", bitmap`
................
................
................
................
................
.......00.......
......000.......
.......00.......
.......00.......
.......00.......
.......00.......
.....000000.....
................
................
................
................`],
  ["2", bitmap`
................
................
................
................
................
......0000......
.....00..00.....
.........00.....
.......000......
......00........
.....00..00.....
.....000000.....
................
................
................
................`],
  ["3", bitmap`
................
................
................
................
................
.....000000.....
........00......
.......00.......
......0000......
.........00.....
.....00..00.....
......0000......
................
................
................
................`],
  [plugTile1, bitmap`
................
....55555555....
...5555555555...
..555555555555..
.55555555555555.
.55555555555555.
.55555555555555.
.55555555555555.
.55555555555555.
.55555555555555.
.55555555555555.
.55555555555555.
..555555555555..
...5555555555...
....55555555....
................`],
  [plugTile2, bitmap`
................
....DDDDDDDD....
...DDDDDDDDDD...
..DDDDDDDDDDDD..
.DDDDDDDDDDDDDD.
.DDDDDDDDDDDDDD.
.DDDDDDDDDDDDDD.
.DDDDDDDDDDDDDD.
.DDDDDDDDDDDDDD.
.DDDDDDDDDDDDDD.
.DDDDDDDDDDDDDD.
.DDDDDDDDDDDDDD.
..DDDDDDDDDDDD..
...DDDDDDDDDD...
....DDDDDDDD....
................`],
  [scramTop, bitmap`
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..`],
  [scramMid, bitmap`
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..
..LLLLLLLLLLLL..`],
  [scramBot, bitmap`
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..
..111111111111..`],
  [light, bitmap`
................
....66666666....
...6666666666...
..666666666666..
.66666666666666.
.66666666666666.
.66666666666666.
.66666666666666.
.66666666666666.
.66666666666666.
.66666666666666.
.66666666666666.
..666666666666..
...6666666666...
....66666666....
................`],
  [blank, bitmap`
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................
................`]
)

setSolids([cursor, divider, blank])

//map definitions
var level
const levels = [
  map`
..........
..R.1.1.1.
..R.2.2.2.
..R.3.3.3.
qwertyuiop
asdfghjkl.
.zxcvbnm..
DDDDDDDDDD
qwertyuiop
asdfghjkl.
Czxcvbnm.X`, //main machine
  map`
...........
...........
..[..[..[..
..|..|..|..
..]..]..]..
.<.><.><.>.
.....C.....
...........`, //wheel configuration
  map`
(...X.....
)...X.....
..........
qwertyuiop
asdfghjkl.
Czxcvbnm..`, //plug configuration
  map`
Lenigma.
.explain
Lwheels.
Lplugs..`, //enigma main menu
  map`
---
NCE
---`, //table with note and enigma
  map`
.....
.....
.....
.....` //note
]

function updateLevel(inputLevel) {
  clearText()
  level = inputLevel
  setMap(levels[level]) //make it look nice
  if (level == 0) {
    updateKeyboard(0, 8, 1)
    updateScramblers()
    if (ciphertext.length != 0) {
      addText(displayCiphertext, {
        x: 3,
        y: 0,
        color: color`3`
      })
      addSprite((getFirst(decapitalize(ciphertext.charAt(ciphertext.length - 1)))).x, (getFirst(decapitalize(ciphertext.charAt(ciphertext.length - 1)))).y, light)
    }
  }
  if (level == 1) {
    updateScramblers()
  }
  if (level == 2) {
    updateKeyboard(0, 3, 0)
    addText("PLUG 1", {
      x: 2,
      y: 3,
      color: color`5`
    })
    addText("PLUG 2", {
      x: 2,
      y: 5,
      color: color`D`
    })
  }
  if (level == 3) {
    addSprite(0, 0, cursor)
    if (explain == true) {
      addSprite(0, 1, checkmark)
    } else {
      addSprite(0, 1, redX)
    }
  }
  if (level == 4) {
    setBackground(table)
  } else {
    setBackground(blank)
  }
  if (level == 5) {
    if (tutorialLevel == 0) {
      addText("Encode:", {
        x: 1,
        y: 1,
        color: color`0`
      })
      addText("enigma", {
        x: 1,
        y: 2,
        color: color`0`
      })
      addText("Order: 1, 2, 3", {
        x: 1,
        y: 4,
        color: color`0`
      })
      addText("Rotation: a, a, a", {
        x: 1,
        y: 5,
        color: color`0`
      })
      addText("Plugs: None", {
        x: 1,
        y: 6,
        color: color`0`
      })
    }
    else if (tutorialLevel == 1) {
      addSprite(0, 0, checkmark)
      addText("AKXINU", {
        x: 4,
        y: 0,
        color: color`0`
      })

      addText("Decode:", {
        x: 1,
        y: 4,
        color: color`0`
      })
      addText("DQBDNKVTTP", {
        x: 1,
        y: 5,
        color: color`0`
      })
      addText("Order: 1, 2, 3", {
        x: 1,
        y: 7,
        color: color`0`
      })
      addText("Rotation: w, m, u", {
        x: 1,
        y: 8,
        color: color`0`
      })
      addText("Plugs: None", {
        x: 1,
        y: 9,
        color: color`0`
      })
    }
    else if (tutorialLevel == 2) {
      addSprite(0, 0, checkmark)
      addText("AKXINU", {
        x: 4,
        y: 0,
        color: color`0`
      })
      addText("helloworld", {
        x: 4,
        y: 1,
        color: color`0`
      })
      
      addText("Encode:", {
        x: 1,
        y: 4,
        color: color`0`
      })
      addText("secretsgohere", {
        x: 1,
        y: 5,
        color: color`0`
      })
      addText("Order: 1, 2, 3", {
        x: 1,
        y: 7,
        color: color`0`
      })
      addText("Rotation: n, o, m", {
        x: 1,
        y: 8,
        color: color`0`
      })
      addText("Plugs: e > z", {
        x: 1,
        y: 9,
        color: color`0`
      })
    }
    else if (tutorialLevel == 3) {
      addSprite(0, 0, checkmark)
      addText("AKXINU", {
        x: 4,
        y: 0,
        color: color`0`
      })
      addText("helloworld", {
        x: 4,
        y: 1,
        color: color`0`
      })
      addText("ESGHAGZMKLVNC", {
        x: 4,
        y: 2,
        color: color`0`
      })
      
      addText("Decode:", {
        x: 1,
        y: 4,
        color: color`0`
      })
      addText("WLJXGJJREQXFAWTB", {
        x: 1,
        y: 5,
        color: color`0`
      })
      addText("Order: 3, 1, 2", {
        x: 1,
        y: 7,
        color: color`0`
      })
      addText("Rotation: u, f, n", {
        x: 1,
        y: 8,
        color: color`0`
      })
      addText("Plugs: f > o, i > c", {
        x: 1,
        y: 9,
        color: color`0`
      })
    }
    
    addText("Wheel order and", {
      x: 1,
      y: 14,
      color: color`0`
    })
    addText("rotation are R to L", {
      x: 1,
      y: 15,
      color: color`0`
    })
    
    if (tutorialLevel == 4) {
      clearText()
      addSprite(0, 0, checkmark)
      addText("AKXINU", {
        x: 4,
        y: 0,
        color: color`0`
      })
      addText("helloworld", {
        x: 4,
        y: 1,
        color: color`0`
      })
      addText("ESGHAGZMKLVNC", {
        x: 4,
        y: 2,
        color: color`0`
      })
      
      addText("Thanks for", {
        x: 1,
        y: 4,
        color: color`0`
      })
      addText("playing!", {
        x: 1,
        y: 5,
        color: color`0`
      })
      addText("There's no more", {
        x: 1,
        y: 7,
        color: color`0`
      })
      addText('"game", exactly,', {
        x: 1,
        y: 8,
        color: color`0`
      })
      addText("but feel free to", {
        x: 1,
        y: 9,
        color: color`0`
      })
      addText("use the machine", {
        x: 1,
        y: 10,
        color: color`0`
      })
      addText("for your own", {
        x: 1,
        y: 11,
        color: color`0`
      })
      addText("purposes. I won't", {
        x: 1,
        y: 12,
        color: color`0`
      })
      addText("ask what they are.", {
        x: 1,
        y: 13,
        color: color`0`
      })
    }
  }
}

//clearTile, but it doesn't remove the cursor or the button sprite
//if keepLetters is true, also doesn't remove letter tiles
function safeClearTile(x, y, keepLetters) {
  if (keepLetters == true) {
    let spritesToRemove = getTile(x, y).filter(sprite => sprite.type != cursor && sprite.type != button && (sprite.type.charCodeAt(0) < 97 || sprite.type.charCodeAt(0) > 122))
    spritesToRemove.forEach(sprite => sprite.remove())
  } else {
    let spritesToRemove = getTile(x, y).filter(sprite => sprite.type != cursor && sprite.type != button)
    spritesToRemove.forEach(sprite => sprite.remove())
  }
}

const alphabet = "abcdefghijklmnopqrstuvwxyz" //it's just the alphabet.
const alphabetCapital = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" //alphabet but capitalized
//number to letter
function alphabetize(inNumber) { return alphabet.charAt(inNumber - 1) }
//letter to number
function dealphabetize(inCharacter) { return (alphabet.indexOf(inCharacter) + 1) }
//makes a lowercase letter uppercase
function capitalize(inCharacter) { return (alphabetCapital.charAt(alphabet.indexOf(inCharacter))) }
//makes an uppercase letter lowercase
function decapitalize(inCharacter) { return (alphabet.charAt(alphabetCapital.indexOf(inCharacter))) }
//puts a number within alphabet if it's at most 1 alphabet away
function constrain(constrainee) {
  if (constrainee < 1) { return constrainee + 26 } else if (constrainee > 26) { return constrainee - 26 } else return constrainee
}

var explain = false //boolean, when true machine goes step-by-step with explanations
var explanation = "" //string used to explain things
function explanationText(text) {
  addText(text, {
    x: 3,
    y: 0,
    color: color`3`
  })
}
var explanationStep = 0

var wheelOrderConfig = 0 //for scrambler config, first scrambler to be swapped with another
var wheelNum = 0 //for scrambler config, number being selected/swapped. 0 for off

var plugSelected = 0 //for plug config, 1 or 2, or 0 for off. plug kind you're messing with

var tutorialLevel = 0 //step of the tutorial user is on
const tutorialObjective = ["AKXINU", "HELLOWORLD", "ESGHAGZMKLVNC", "THANKSFORPLAYING"] //desired output of each tutorial step

var letter //number value of inputted letter, 1-26

var rotations = [0, 0, 0] //rotations of scramblers in wheel order, 0-25

var plug1 = 0 //plugboard, swaps with plug2 at beginning and end, 1-26 or 0 for off
var plug2 = 0 //plugboard, swaps with plug1
var plug3 = 0 //plugboard, swaps with plug4
var plug4 = 0 //plugboard, swaps with plug3

var wheelOrder = [1, 2, 3]

const scramList1 = [17, 6, 4, 22, 8, 26, 7, 3, 11, 14, 20, 18, 10, 19, 9, 23, 12, 21, 16, 1, 5, 24, 2, 13, 25, 15] //sequential array of scrambler 1's 26 outputs at rotation 0. remember, arrays start at 0
const scramList2 = [3, 17, 16, 12, 14, 5, 8, 22, 9, 19, 2, 23, 10, 15, 7, 26, 24, 18, 21, 25, 6, 11, 13, 20, 4, 1]
const scramList3 = [3, 23, 14, 1, 11, 6, 9, 2, 5, 18, 10, 24, 12, 13, 16, 21, 8, 22, 25, 17, 4, 15, 20, 26, 19, 7]

const reflectList = [21, 17, 11, 10, 6, 25, 15, 26, 18, 22, 14, 3, 9, 23, 8, 24, 5, 16, 13, 20, 7, 19, 2, 4, 12, 1] //even indexes go to the next on the array, odd go back (starting at 0)

var ciphertext = ""
var displayCiphertext = ""


function scrambler1(rot, reflected) {
  //account for scrambler's rotation
  let scramLetter = constrain(letter + rot)
  //if explaining, add the path the signal takes
  if (explain == true) { explanation += alphabetize(scramLetter) }
  //enigmatize that thang
  if (reflected == false) { letter = scramList1[scramLetter - 1] - rot } else { letter = scramList1.indexOf(scramLetter) + 1 - rot }
  //there's only so much alphabet
  letter = constrain(letter)
}

function scrambler2(rot, reflected) {
  let scramLetter = constrain(letter + rot)
  if (explain == true) { explanation += alphabetize(scramLetter) }
  if (reflected == false) { letter = scramList2[scramLetter - 1] - rot } else { letter = scramList2.indexOf(scramLetter) + 1 - rot }
  letter = constrain(letter)
}

function scrambler3(rot, reflected) {
  let scramLetter = constrain(letter + rot)
  if (explain == true) { explanation += alphabetize(scramLetter) }
  if (reflected == false) { letter = scramList3[scramLetter - 1] - rot } else { letter = scramList3.indexOf(scramLetter) + 1 - rot }
  letter = constrain(letter)
}

function plugboard() {
  if (letter == plug1) { letter = plug2 } else if (letter == plug2) { letter = plug1 } else if (letter == plug3) { letter = plug4 } else if (letter == plug4) { letter = plug3 }
}

function scramble(reflected) {
  for (let letterPos = 0 + (2 * reflected); letterPos < 3 && letterPos >= reflected - 1; letterPos += 1 - (2 * reflected)) {
    //check scrambler of current position, execute, change position, repeat three times.
    //if reflected true, start at third scrambler and go backwards
    if (wheelOrder[letterPos] == 1) {
      scrambler1(rotations[letterPos], reflected)
    } else if (wheelOrder[letterPos] == 2) {
      scrambler2(rotations[letterPos], reflected)
    } else if (wheelOrder[letterPos] == 3) {
      scrambler3(rotations[letterPos], reflected)
    }
  }
}

function reflector() {
  if (reflectList.indexOf(letter) % 2 == 0) { letter = reflectList[reflectList.indexOf(letter) + 1]; } //if letter's position in array is even, move to next index
  else { letter = reflectList[reflectList.indexOf(letter) - 1] } //if letter's position in array is odd, move down an index
}

function rotate() {
  if (rotations[0] != 25) { rotations[0]++ } else {
    rotations[0] = 0
    if (rotations[1] != 25) { rotations[1]++ } else {
      rotations[1] = 0
      if (rotations[2] != 25) { rotations[2]++ } else { rotations[2] = 0 }
    }
  }
}

function executeEnigma() {
  plugboard()
  scramble(false)
  reflector()
  scramble(true)
  plugboard()
  rotate()
}

//change letters on scramblers to match rotations
function updateScramblers() {
  if (level == 0) {
    for (let scramblerNum = 0; scramblerNum <= 2; scramblerNum++) {
      for (let scramblerY = 1; scramblerY <= 3; scramblerY++) {
        safeClearTile((8 - (2 * scramblerNum)), scramblerY, false)
        if (scramblerY == 1) {
          addSprite((8 - (2 * scramblerNum)), 1, scramTop)
        } else if (scramblerY == 2) {
          addSprite((8 - (2 * scramblerNum)), 2, scramMid)
        } else {
          addSprite((8 - (2 * scramblerNum)), 3, scramBot)
        }
        addSprite((8 - (2 * scramblerNum)), scramblerY, alphabetize(constrain(rotations[scramblerNum] + 3 - scramblerY)))
      }
    }
  }
  if (level == 1) {
    for (let scramblerNum = 0; scramblerNum <= 2; scramblerNum++) {
      for (let scramblerY = 2; scramblerY <= 4; scramblerY++) {
        safeClearTile((8 - (3 * scramblerNum)), scramblerY, false)
        if (scramblerY == 2) {
          addSprite((8 - (3 * scramblerNum)), 2, scramTop)
        } else if (scramblerY == 3) {
          addSprite((8 - (3 * scramblerNum)), 3, scramMid)
        } else {
          addSprite((8 - (3 * scramblerNum)), 4, scramBot)
        }
        addSprite((8 - (3 * scramblerNum)), scramblerY, alphabetize(constrain(rotations[scramblerNum] + 4 - scramblerY)))
      }
    }
    safeClearTile(8, 1, false)
    addSprite(8, 1, wheelOrder[0])
    safeClearTile(5, 1, false)
    addSprite(5, 1, wheelOrder[1])
    safeClearTile(2, 1, false)
    addSprite(2, 1, wheelOrder[2])
    if (wheelOrderConfig != 0) {
      addSprite(getFirst(wheelOrderConfig).x, 1, light)
    }
  }
}

//change lights on keyboard to match plugs
//third input is the index of the letters in getAll, so the plugs go on the right spot
function updateKeyboard(x, y, letterIndex) {
  addSprite(x, y, button)
  addSprite(x + 1, y, button)
  addSprite(x + 2, y, button)
  addSprite(x + 3, y, button)
  addSprite(x + 4, y, button)
  addSprite(x + 5, y, button)
  addSprite(x + 6, y, button)
  addSprite(x + 7, y, button)
  addSprite(x + 8, y, button)
  addSprite(x + 9, y, button)
  addSprite(x + 0, y + 1, button)
  addSprite(x + 1, y + 1, button)
  addSprite(x + 2, y + 1, button)
  addSprite(x + 3, y + 1, button)
  addSprite(x + 4, y + 1, button)
  addSprite(x + 5, y + 1, button)
  addSprite(x + 6, y + 1, button)
  addSprite(x + 7, y + 1, button)
  addSprite(x + 8, y + 1, button)
  addSprite(x + 1, y + 2, button)
  addSprite(x + 2, y + 2, button)
  addSprite(x + 3, y + 2, button)
  addSprite(x + 4, y + 2, button)
  addSprite(x + 5, y + 2, button)
  addSprite(x + 6, y + 2, button)
  addSprite(x + 7, y + 2, button)
  safeClearTile(x, y, true)
  safeClearTile(x + 1, y, true)
  safeClearTile(x + 2, y, true)
  safeClearTile(x + 3, y, true)
  safeClearTile(x + 4, y, true)
  safeClearTile(x + 5, y, true)
  safeClearTile(x + 6, y, true)
  safeClearTile(x + 7, y, true)
  safeClearTile(x + 8, y, true)
  safeClearTile(x + 9, y, true)
  safeClearTile(x + 0, y + 1, true)
  safeClearTile(x + 1, y + 1, true)
  safeClearTile(x + 2, y + 1, true)
  safeClearTile(x + 3, y + 1, true)
  safeClearTile(x + 4, y + 1, true)
  safeClearTile(x + 5, y + 1, true)
  safeClearTile(x + 6, y + 1, true)
  safeClearTile(x + 7, y + 1, true)
  safeClearTile(x + 8, y + 1, true)
  safeClearTile(x + 1, y + 2, true)
  safeClearTile(x + 2, y + 2, true)
  safeClearTile(x + 3, y + 2, true)
  safeClearTile(x + 4, y + 2, true)
  safeClearTile(x + 5, y + 2, true)
  safeClearTile(x + 6, y + 2, true)
  safeClearTile(x + 7, y + 2, true)
  if (plug1 != 0) {
    addSprite((getAll(alphabetize(plug1))[letterIndex]).x, (getAll(alphabetize(plug1))[letterIndex]).y, plugTile1)
  }
  if (plug2 != 0) {
    addSprite((getAll(alphabetize(plug2))[letterIndex]).x, (getAll(alphabetize(plug2))[letterIndex]).y, plugTile1)
  }
  if (plug3 != 0) {
    addSprite((getAll(alphabetize(plug3))[letterIndex]).x, (getAll(alphabetize(plug3))[letterIndex]).y, plugTile2)
  }
  if (plug4 != 0) {
    addSprite((getAll(alphabetize(plug4))[letterIndex]).x, (getAll(alphabetize(plug4))[letterIndex]).y, plugTile2)
  }
}


//WASD
onInput("w", () => {
  if (level != 5) {
    getFirst(cursor).y -= 1
  }
})
onInput("a", () => {
  if (level != 5) {
    getFirst(cursor).x -= 1
  }
})
onInput("s", () => {
  if (level != 5) {
    getFirst(cursor).y += 1
  }
})
onInput("d", () => {
  if (level != 5 && level != 3) {
    getFirst(cursor).x += 1
  }
})


onInput("l", () => {
  //select input letter
  if (level == 0) {
    //check if it's on the clear button
    if (getFirst(cursor).x == getFirst(redX).x && getFirst(cursor).y == getFirst(redX).y) {
      //clear text at top
      ciphertext = ""
      displayCiphertext = ""
      clearText()
      //remove light
      if (getAll(light).length != 0) {
        getFirst(light).remove()
      }
    }
    else if (explain == false) {
      letter = 0
      for (let letterChecked = 1; letterChecked <= 26 && letter == 0; letterChecked++) {
        //check if cursor x and y are the same as letter x and y
        if (getFirst(cursor).x == getAll(alphabetize(letterChecked))[1].x && getFirst(cursor).y == getAll(alphabetize(letterChecked))[1].y) {
          letter = letterChecked
        }
      }
      //if you aren't on blank space
      if (letter != 0) {
        executeEnigma()
        //get rid of previous light, if there is one
        if (getAll(light).length != 0) {
          getFirst(light).remove()
        }
        //add light to output letter
        addSprite((getFirst(alphabetize(letter))).x, (getFirst(alphabetize(letter))).y, light)
        //start list of outputs or graft new output onto list
        if (ciphertext.length == 0) {
          ciphertext = capitalize(alphabetize(letter))
          displayCiphertext = capitalize(alphabetize(letter))
        } else {
          ciphertext = ciphertext + capitalize(alphabetize(letter))
          displayCiphertext = displayCiphertext + capitalize(alphabetize(letter))
        }
        //if it's too long to fit, trim the first letter from the display
        if (displayCiphertext.length >= 15) { displayCiphertext = displayCiphertext.replace(displayCiphertext.charAt(0), "") }
        //list outputs
        addText(displayCiphertext, {
          x: 3,
          y: 0,
          color: color`3`
        })

        updateScramblers()
      }
    }

    //when explain is true, slower and with explanations
    else {
      if (explanationStep == 0) {
        letter = 0
        for (let letterChecked = 1; letterChecked <= 26 && letter == 0; letterChecked++) {
          //check if cursor x and y are the same as letter x and y
          if (getFirst(cursor).x == getAll(alphabetize(letterChecked))[1].x && getFirst(cursor).y == getAll(alphabetize(letterChecked))[1].y) {
            letter = letterChecked
          }
        }

        //if you aren't on blank space
        if (letter != 0) {
          //get rid of line of ciphertext, if it's there
          clearText()
          explanationStep++
        }
      }

      //should follow directly after step 0, if step 0's conditions were met
      if (explanationStep == 1) {
        //execute enigma but with pauses and explanations
        explanation = alphabetize(letter) + ">plug>"
        plugboard()
        explanation += alphabetize(letter)
        clearText()
        explanationText(explanation)
        explanationStep++
      }

      //like scramble function, but with explanationStep - 2 instead of letterPos
      else if (explanationStep >= 2 && explanationStep <= 4) {
        explanation = alphabetize(letter)
        if (wheelOrder[explanationStep - 2] == 1) {
          explanation += ">wheel1"
          scrambler1(rotations[explanationStep - 2], false)
        } else if (wheelOrder[explanationStep - 2] == 2) {
          explanation += ">wheel2"
          scrambler2(rotations[explanationStep - 2], false)
        } else if (wheelOrder[explanationStep - 2] == 3) {
          explanation += ">wheel3"
          scrambler3(rotations[explanationStep - 2], false)
        }
        explanation += ">" + alphabetize(letter)
        clearText()
        explanationText(explanation)
        explanationStep++
      } else if (explanationStep == 5) {
        explanation = alphabetize(letter) + ">reflector>"
        reflector()
        explanation += alphabetize(letter)
        clearText()
        explanationText(explanation)
        explanationStep++
      }

      //like reflected scramble, but with 8 - explanationStep instead of letterPos
      else if (explanationStep >= 6 && explanationStep <= 8) {
        explanation = alphabetize(letter)
        if (wheelOrder[8 - explanationStep] == 1) {
          explanation += ">wheel1"
          scrambler1(rotations[8 - explanationStep], true)
        } else if (wheelOrder[8 - explanationStep] == 2) {
          explanation += ">wheel2"
          scrambler2(rotations[8 - explanationStep], true)
        } else if (wheelOrder[8 - explanationStep] == 3) {
          explanation += ">wheel3"
          scrambler3(rotations[8 - explanationStep], true)
        }
        explanation += "R>" + alphabetize(letter)
        clearText()
        explanationText(explanation)
        explanationStep++
      } else if (explanationStep == 9) {
        //plugboard
        explanation = alphabetize(letter) + ">plug>"
        plugboard()
        explanation += capitalize(alphabetize(letter))

        //get rid of previous light, if there is one
        if (getAll(light).length != 0) {
          getFirst(light).remove()
        }
        //add light to output letter
        addSprite((getFirst(alphabetize(letter))).x, (getFirst(alphabetize(letter))).y, light)
        //start list of outputs or graft new output onto list
        if (ciphertext.length == 0) {
          ciphertext = capitalize(alphabetize(letter))
          displayCiphertext = capitalize(alphabetize(letter))
        } else {
          ciphertext = ciphertext + capitalize(alphabetize(letter))
          displayCiphertext = displayCiphertext + capitalize(alphabetize(letter))
        }
        //if it's too long to fit, trim the first letter from the display
        if (displayCiphertext.length >= 15) { displayCiphertext = displayCiphertext.replace(displayCiphertext.charAt(0), "") }

        clearText()
        explanationText(explanation)
        explanationStep++
      } else if (explanationStep == 10) {
        rotate()
        updateScramblers()
        explanationText("wheels rotate")
        explanationStep++
      } else if (explanationStep == 11) {
        clearText()
        //list outputs
        addText(displayCiphertext, {
          x: 3,
          y: 0,
          color: color`3`
        })
        explanationStep = 0
      }
    }

    //check if ciphertext contains the current objective for the tutorial
    if (ciphertext.includes(tutorialObjective[tutorialLevel]) == true) {
      tutorialLevel++
    }
  } else if (level == 1) {
    //check if it's on arrows and scroll scrambler rotation
    for (let leftChecked = 0; leftChecked <= 2; leftChecked++) {
      //check if cursor x and y are the same as left arrow x and y
      if (getFirst(cursor).x == getAll(arrowL)[2 - leftChecked].x && getFirst(cursor).y == getAll(arrowL)[2 - leftChecked].y) {
        rotations[leftChecked] = constrain(rotations[leftChecked]) - 1
        updateScramblers()
      }
    }
    for (let rightChecked = 0; rightChecked <= 2; rightChecked++) {
      //check if cursor x and y are the same as right arrow x and y
      if (getFirst(cursor).x == getAll(arrowR)[2 - rightChecked].x && getFirst(cursor).y == getAll(arrowR)[2 - rightChecked].y) {
        rotations[rightChecked] = constrain(rotations[rightChecked] + 2) - 1
        updateScramblers()
      }
    }

    //check if cursor is on number, swap or prepare to swap with another number
    let exit = false
    for (wheelNum = 1; wheelNum <= 3 && exit == false; wheelNum++) {
      //check if cursor x and y are the same as wheel number x and y
      if (getFirst(cursor).x == getFirst(wheelNum).x && getFirst(cursor).y == getFirst(wheelNum).y) {
        //if nothing's lit up, light the number you're on up
        if (wheelOrderConfig == 0) {
          wheelOrderConfig = wheelNum
        }
        //if something's lit up, swap it with number you pressed
        else {
          let wheelNumIndex = wheelOrder.indexOf(wheelNum)
          wheelOrder[wheelOrder.indexOf(wheelOrderConfig)] = wheelNum
          wheelOrder[wheelNumIndex] = wheelOrderConfig
          wheelOrderConfig = 0
        }
        updateScramblers()
        //so that it doesn't keep running and mess with things
        exit = true
      }
    }
  } else if (level == 2) {
    //check if it's on plug 1 select or clear thingies
    if (getFirst(cursor).x == getFirst(plugTile1).x && getFirst(cursor).y == getFirst(plugTile1).y) {
      plugSelected = 1
    } else if (getFirst(cursor).x == getAll(redX)[0].x && getFirst(cursor).y == getAll(redX)[0].y) {
      plug1 = 0
      plug2 = 0
      updateKeyboard(0, 3, 0)
    }

    //check if it's on plug 2 select or clear thingies
    else if (getFirst(cursor).x == getFirst(plugTile2).x && getFirst(cursor).y == getFirst(plugTile2).y) {
      plugSelected = 2
    } else if (getFirst(cursor).x == getAll(redX)[1].x && getFirst(cursor).y == getAll(redX)[1].y) {
      plug3 = 0
      plug4 = 0
      updateKeyboard(0, 3, 0)
    }

    //check if it's on a letter and that it isn't already part of a plug
    else {
      for (let letterChecked = 1; letterChecked <= 26; letterChecked++) {
        if (getFirst(cursor).x == getFirst(alphabetize(letterChecked)).x && getFirst(cursor).y == getFirst(alphabetize(letterChecked)).y && (letterChecked != plug1 && letterChecked != plug2 && letterChecked != plug3 && letterChecked != plug4)) {
          if (plugSelected == 1) {
            if (plug1 == 0) {
              plug1 = letterChecked
            } else if (plug2 == 0) {
              plug2 = letterChecked
            } else {
              plug1 = letterChecked
              plug2 = 0
            }
          } else if (plugSelected == 2) {
            if (plug3 == 0) {
              plug3 = letterChecked
            } else if (plug4 == 0) {
              plug4 = letterChecked
            } else {
              plug3 = letterChecked
              plug4 = 0
            }
          }
          updateKeyboard(0, 3, 0)
        }
      }
    }
  } else if (level == 3) {
    //check which level the cursor is on and go to it
    //enigma
    if (getFirst(cursor).y == getAll(light)[0].y) {
      updateLevel(0)
    }
    //wheels
    else if (getFirst(cursor).y == getAll(light)[1].y) {
      updateLevel(1)
    }
    //plugs
    else if (getFirst(cursor).y == getAll(light)[2].y) {
      updateLevel(2)
    }

    //if cursor is on explain toggle, toggle explain and update tile
    if (getFirst(cursor).y == 1) {
      if (explain == true) {
        explain = false
        safeClearTile(0, 1, false)
        addSprite(0, 1, redX)
      } else {
        explain = true
        safeClearTile(0, 1, false)
        addSprite(0, 1, checkmark)
      }
    }
  } else if (level == 4) {
    if (getFirst(cursor).x == getFirst(enigma).x) {
      updateLevel(3)
    } else if (getFirst(cursor).x == getFirst(note).x) {
      updateLevel(5)
    }
  }
})

//exit to level select
onInput("j", () => {
  if (level <= 2) {
    if (level == 0 && explain == true) {
      if (explanationStep == 10) {
        rotate()
      }
      explanationStep = 0
      letter = 0
    }
    if (level == 2) {
      plugSelected = 0
      if (plug1 != 0 && plug2 == 0) {
        plug1 = 0
      }
      if (plug3 != 0 && plug4 == 0) {
        plug3 = 0
      }
    }
    updateLevel(3)
  } else if (level != 4) {
    updateLevel(4)
  }
})

//initialize
updateLevel(4)

